﻿//============================================================================================
/*
 * @brief 数学计算。
 * 
 * 概率计算：
 * 20 个小球中，有 3 个红球，一次抽取 4 个球，至少有一个是红球的概率是：
 * 1 - 抽不到的概率 = 1 - C(4, 17) / C(4, 20)  = 50.877%
 * 
 *********************************************************************************************/

namespace Tszy.Unlimited.Base.Extensions;

/// <summary>
/// 数学计算扩展。
/// </summary>
public static class MathExtensions
{
    #region 阶乘

    /// <summary>
    /// 计算指定数字的阶乘。
    /// </summary>
    /// <remarks>
    /// n! = 1 × 2 × 3 × …… × （n - 1） × n
    /// 示例：
    /// 5! = 1 × 2 × 3 × 4 × 5 = 120
    /// </remarks>
    /// <param name="source"></param>
    /// <returns></returns>
    public static long Factorial(this long source)
    {
        int result = 1;

        for (int i = 1; i <= source; i++)
        {
            result *= i;
        }

        return result;
    }

    /// <summary>
    /// 计算指定数字的阶乘。
    /// </summary>
    /// <remarks>
    /// n! = 1 × 2 × 3 × …… × （n - 1） × n
    /// 示例：
    /// 5! = 1 × 2 × 3 × 4 × 5 = 120
    /// </remarks>
    /// <param name="source"></param>
    /// <returns></returns>
    public static int Factorial(this int source)
    {
        int result = 1;

        for (int i = 1; i <= source; i++)
        {
            result *= i;
        }

        return result;
    }

    /// <summary>
    /// 计算指定数字的阶乘。
    /// </summary>
    /// <remarks>
    /// n! = 1 × 2 × 3 × …… × （n - 1） × n
    /// 示例：
    /// 5! = 1 × 2 × 3 × 4 × 5 = 120
    /// </remarks>
    /// <param name="source"></param>
    /// <returns></returns>
    public static decimal Factorial(this decimal source)
    {
        decimal result = 1;

        for (decimal i = 1; i <= source; i++)
        {
            result *= i;
        }

        return result;
    }

    /// <summary>
    /// 计算指定数字的阶乘。
    /// </summary>
    /// <remarks>
    /// n! = 1 × 2 × 3 × …… × （n - 1） × n
    /// 示例：
    /// 5! = 1 × 2 × 3 × 4 × 5 = 120
    /// </remarks>
    /// <param name="source"></param>
    /// <returns></returns>
    public static float Factorial(this float source)
    {
        float result = 1;

        for (float i = 1; i <= source; i++)
        {
            result *= i;
        }

        return result;
    }

    /// <summary>
    /// 计算指定数字的阶乘。
    /// </summary>
    /// <remarks>
    /// n! = 1 × 2 × 3 × …… × （n - 1） × n
    /// 示例：
    /// 5! = 1 × 2 × 3 × 4 × 5 = 120
    /// </remarks>
    /// <param name="source"></param>
    /// <returns></returns>
    public static double Factorial(this double source)
    {
        double result = 1;

        for (double i = 1; i <= source; i++)
        {
            result *= i;
        }

        return result;
    }

    #endregion 阶乘

    #region 排列

    /// <summary>
    /// 计算排列数。
    /// </summary>
    /// <param name="source">元素总数。</param>
    /// <param name="target">取出的元素个数。</param>
    /// <returns>排列数。</returns>
    public static long Arrangement(this long source, long target)
    {
        return source.Factorial() / (source - target).Factorial();
    }

    /// <summary>
    /// 计算排列数。
    /// </summary>
    /// <param name="source">元素总数。</param>
    /// <param name="target">取出的元素个数。</param>
    /// <returns>排列数。</returns>
    public static int Arrangement(this int source, int target)
    {
        return source.Factorial() / (source - target).Factorial();
    }

    /// <summary>
    /// 计算排列数。
    /// </summary>
    /// <param name="source">元素总数。</param>
    /// <param name="target">取出的元素个数。</param>
    /// <returns>排列数。</returns>
    public static decimal Arrangement(this decimal source, decimal target)
    {
        return source.Factorial() / (source - target).Factorial();
    }

    /// <summary>
    /// 计算排列数。
    /// </summary>
    /// <param name="source">元素总数。</param>
    /// <param name="target">取出的元素个数。</param>
    /// <returns>排列数。</returns>
    public static float Arrangement(this float source, float target)
    {
        return source.Factorial() / (source - target).Factorial();
    }

    /// <summary>
    /// 计算排列数。
    /// </summary>
    /// <param name="source">元素总数。</param>
    /// <param name="target">取出的元素个数。</param>
    /// <returns>排列数。</returns>
    public static double Arrangement(this double source, double target)
    {
        return source.Factorial() / (source - target).Factorial();
    }

    #endregion 排列

    #region 组合

    /// <summary>
    /// 计算组合数。
    /// </summary>
    /// <param name="source">元素总数。</param>
    /// <param name="target">取出的元素个数。</param>
    /// <returns>组合数。</returns>
    public static decimal Combination(this decimal source, decimal target)
    {
        return source.Arrangement(target) / target.Arrangement(target);
    }

    /// <summary>
    /// 计算组合数。
    /// </summary>
    /// <param name="source">元素总数。</param>
    /// <param name="target">取出的元素个数。</param>
    /// <returns>组合数。</returns>
    public static double Combination(this double source, double target)
    {
        return source.Arrangement(target) / target.Arrangement(target);
        //// 排列数。
        //var a = source.Arrangement(target);
        //// 取出的元素个数的阶乘。
        //var mF = target.Factorial();

        //return a / mF;
    }

    /// <summary>
    /// 组合乘法。
    /// </summary>
    /// <remarks>
    /// 1 → 4 → 7
    /// 2 ↘ 5 ↘ 8
    /// 3    6    9
    ///
    /// 即：
    /// r1 = 1 × 4 × 7
    /// r2 = 1 × 4 × 8
    /// r3 = 1 × 4 × 9
    /// r4 = 1 × 5 × 7
    /// ……
    /// rn = 3 × 6 × 9
    /// </remarks>
    /// <param name="source"></param>
    /// <returns></returns>
    public static IEnumerable<double> CombinationMul(this List<List<double>> source)
    {
        return source.OnCombinationMul().ToList();
    }

    private static IEnumerable<double> OnCombinationMul(this List<List<double>> source, int index = 0)
    {
        foreach (var sourceItem in source[index])
        {
            var index2 = index + 1;

            if (source.Count == index2 + 1)
            {
                foreach (var resultValue in source[index2])
                {
                    var result = sourceItem * resultValue;

                    yield return result;
                }
            }
            else
            {
                foreach (var resultValue in OnCombinationMul(source, index2))
                {
                    var result = sourceItem * resultValue;

                    yield return result;
                }
            }
        }
    }

    #endregion 组合
}
